/* 
watcher观察者，用于发射更新的行为
vm Object JGVue的实例
expOrfn {string | function}
expOrfn：渲染watcher，传入的渲染函数，
如果是计算watcher，传入的路径表达式.监听'a.b.c'
暂时只考虑expOrfn为函数的情况


之前将渲染watcher放在全局作用域下，这样处理看起来的简单，但是是有问题的
vue项目中包含很多的组件，各个组件是自治（自我管理）
那么watcher可能会有多个，去处理不同的事情，每个watcher用于描述一个渲染行为或者计算行为
如子组件发生数据的更新，页面需要重新渲染（真正的vue中是局部渲染）,是渲染watcher
如vue中推荐的是使用计算属性代替复杂的插值表达式，是计算watcher
计算属性会伴随其使用的属性的变化而变化，name会缓存
name:() => this.firstname + this.lastname
计算属性 name依赖于属性firstname和属性lastname
只要被依赖的属性发生变化，那么就会促使计算属性 重新计算watcher

依赖收集与派发更新是怎么运行起来的？？？？
页面有三个组件，分为维护name，age，gender属性，第一次在渲染的时候，
这三个组件的属性都会被访问到，只要被访问了都会对其进行依赖收集。
第一次收集的是name，age，gender三个属性
进行其中一个组件的更新时，如name组件，就会进行派发更新，就会去第一次收集
中查看，收集了什么就更新什么，但是age和gender是没发生改变，再次访问的时候不会
进行读取，所以不需要进行
更新完成之后页面需要进行局部刷新，这里刷新的只有name，派发更新，进行第二次收集，
只会收集name，
但是如果再次改变name的时候，进行派发更新，更新什么东西，会去二次收集中查找，
收集什么更新什么
根本都不会判断age，gender，只会刷新name

访问的时候就会收集，更新的时候只会更新收集到的数据，
vue页面中没有使用到的属性不会进行收集

vue源码中defineRe...在getter方法中会对组件中使用到的属性进行收集，
修改源码，输出“”，创建vue应用，则页面第一次渲染的时候会输出“”，证明收集到了组件中
使用到的数据，但是修改某一个数据的时候，其他的未修改的数据也会跟着一起被重新收集了。
创建vue的其他组件component，com1，由于组件是自治的，因此修改的子组件com1的数据的话
只会watcher更新子组件com1的数据，不会收集父组件的数据。

所谓的依赖收集实际上就是告诉当前的watcher什么属性被访问了，
那么在这个watcher在计算的时候或者渲染页面的时候就会将
这些收集到的属性进行更新

在页面进行渲染的时候，页面的属性一定会被访问到。既然正在渲染中，也就表示
此时的watcher正在调用get还没有结束

如何将属性与当前watcher关联起来？？？
在全局准备一个targetStack（watcher栈，简单理解为watcher “数组”，
把一个操作中需要使用的watcher全都存储起来）
在watcher调用get方法的时候，(get进行计算或者渲染)，
将当前watcher放到全局，在get执行结束的之后，将这个全局的watcher移除，
为了下次更新的时候再次使用这个全局的watcher，提供pushTarget，
popTarget这两个方法

怎么把当前属性和watcher管理起来？？？
每个属性都有一个Dep对象


我们在访问对象属性的时候（get调用时），我们的渲染watcher就在全局中
将属性与watcher关联，其实就是将当前渲染的watcher存储到属性相关的dep中；
同时将dep也存储到watcher全局中（互相引用）

属性引用了当前的渲染watcher，属性知道谁渲染他，
当前渲染watcher引用了访问的属性dep，当前的watcher知道渲染了什么属性

我们的dep还有一个方法notify，内部就是将dep中的subs取出来，依次调用其
updata方法。subs中存储的是知道要渲染什么属性的watcher。

属性和watcher会互相关联。


*/
let watcherid = 0;
class Watcher {
  constructor(vm, expOrfn) {
    // expOrfn是vue中传过来的mount函数，mount是渲染函数，会触发data的get
    this.vm = vm;
    this.getter = expOrfn;
    this.watcherid = watcherid++;
    this.deps = []; // 依赖项
    this.depIds = {}; // 是一个set类型，用于保证依赖性的唯一性(简化的代码暂时不实现这一块)

    // 一开始需要渲染：真实vue中：this.lazy?undefined:this.get()
    this.get()
  }

  // 计算，触发getter
  get() {
    pushTarget(this)
    this.getter.call(this.vm, this.vm) // 上下文的问题就解决了
    popTarget(this)
  }

  /*
  执行，并判断是懒加载，还是同步执行，还是异步执行
  我们现在只考虑 异步执行 (简化的是同步执行)
  */
 run() {
   this.get()
   // 在真正的vue中是调用queueWatcher,来触发nextTick进行异步的执行
 }

 /*对外公开的函数，用于在熟悉发生变化时触发的接口*/
 update() {
   this.run()
 }

 // 清空依赖队列
 cleanupDep() {

 }

 // 将当前的dep与当前的watcher关联
 addDep(dep) {
  this.deps.push(dep)
 }

}


/**
 * 梳理watcher与dep与属性的关系
 * 假设：有三个属性，name， age，gender，页面渲染这三个属性
 * 抽象语法树，页面刚开始为空，(抽象语法树会转换为)vnode,
 * vnode其实是抽象语法树与数据的结合，结合的时候会访问属性的getter
 * 就会进行依赖收集，前提是全局watcher是存在的
 * 数据比如属性name会和dep关联起来，dep会最终和watcher关联在一起，互相引用的关系
 * 除了name的dep之外还会有age的dep，gender的dep，此时案例中watcher存储了三个dep
 * vnode转换成真正的dom，渲染到页面展示，这时get方法执行完成，上边的渲染get方法开始
 * ，get结束，全局watcher就不存在了。属性还在，dep也还在，dep中引用了watcher，
 * 所以可以通过属性找到watcher
 * 
 * 修改father属性，前边没有做依赖收集，也关联了dep，但是没有依赖收集所以没有关联watcher，
 * 改变father没有任何相应变化。
 * 如果修改name属性，有dep，且关联了watcher，就可以找到watcher调用update，页面更细，
 * 
 */